package com.szwistar.common.jnlua;

import java.io.File;
import java.io.FileInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.naef.jnlua.LuaState;
import com.naef.jnlua.NamedJavaFunction;
import com.szwistar.common.datastruct.Utils;

public class ThreadLocalLuaState extends LuaState {
	static final Logger LOG = LoggerFactory.getLogger(ThreadLocalLuaState.class);
	static final Logger LOGLUA = LoggerFactory.getLogger("lua");

	// 用于保存线程专用 LuaState 的线程局部存储
	private static final ThreadLocal<ThreadLocalLuaState> threadLuaState = new ThreadLocal<ThreadLocalLuaState>();
	// LuaState 初始化回调接口
	private static LuaStateInitializer luaStateInitializer = null;

	/// 实现print命令
	protected static class JLFunc_print implements NamedJavaFunction {
        @Override
        public String getName() { return "print"; }

        @Override
        public int invoke(LuaState luaState) {
            String msg = luaState.checkString(1);
            LOGLUA.info(msg);
            return 0;
        }
    }

	/**
	 * 设置 LuaState 初始化回调接口
	 * @param luaStateInitializer
	 */
	public static void setLuaStateInitializer(LuaStateInitializer luaStateInitializer) {
		ThreadLocalLuaState.luaStateInitializer = luaStateInitializer;
	}

	/**
	 * 获取当前线程绑定的 Lua 执行环境
	 * 注意：因为 LuaState 不能跨线程访问，所以如果某个线程需要执行脚本，就必须创建自己的 LuaState
	 */
	public static ThreadLocalLuaState get() {
		// 从线程局部存储中获取当前线程的 LuaState，如没有，就说明是第一次访问，需要为此线程新建一个
		ThreadLocalLuaState lua = threadLuaState.get();
		if(lua == null) {
			LOG.debug("!!!初始化线程 {}#{} 内的 Lua 解释器 ...", Thread.currentThread().getId(), Thread.currentThread().getName());

			// 新建 LuaState
			lua = new ThreadLocalLuaState();
			// 保存到线程局部存储
			threadLuaState.set(lua);

			// 打开 jnlua 标准java包
			lua.openLibs();

			// 注册扩展函数和包
	        lua.register(new JLFunc_print());

	        // 调用初始化接口进行额外的初始化
	        if(luaStateInitializer != null) {
	        	luaStateInitializer.init(lua);
	        }

			LOG.debug("!!!初始化 Lua 解释器成功！");
		}

		return lua;
	}

	public void setGlobal(String varName, Object value) {
		pushJavaObjectRaw(value);
		setGlobal(varName);
	}

	/**
	 * 执行 Lua 脚本
	 * @param script
	 * @return
	 */
	public Object execLua(String script) throws Exception {
		if(Utils.isEmpty(script)) { return null; }
		LOG.debug("!!!在线程 {}#{} 中执行 Lua 脚本: \n\t{}", Thread.currentThread().getId(), Thread.currentThread().getName(), script);

		// 加载脚本（实际上加载为一个函数）
		load(script, "_script@"+Thread.currentThread().getName());

		// 让此脚本在全局 level 上执行？
		//getFEnv(GLOBALSINDEX);
		//setFEnv(-2);

		// 执行脚本函数
		// 0: 无参数
		// 1: 表示有1个返回值
		call(0, 1);

		// 返回结果
		//return toJavaObjectRaw(-1);
		//return toJavaObject(-1, Object.class);
		Object ret = toJavaObject(-1, Object.class);
		LOG.debug("!!!执行 Lua 脚本返回值类型: {}", (ret == null ? "null" : ret.getClass().getSimpleName()));
		return ret;
	}

	/**
	 * 执行 Lua 脚本文件
	 * @return
	 * @throws Exception
	 */
	public Object execLua(File scriptFile) throws Exception {
		LOG.debug("在线程 {} 中执行 Lua 脚本文件: \n\t'{}'", Thread.currentThread().getName(), scriptFile);

		// 加载脚本（实际上加载为一个函数）
		load(new FileInputStream(scriptFile), scriptFile.getCanonicalPath());

		// 执行脚本函数
		call(0, 1);

		// 返回结果
		//return toJavaObjectRaw(-1);
		return toJavaObject(-1, Object.class);
	}
}
